/*
 * Copyright 2010, 2011 Matt Oliveri
 *
 * This file is part of JConfigGen
 *
 * JConfigGen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JConfigGen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with JConfigGen.  If not, see <http://www.gnu.org/licenses/>.
 */

package atma.jconfiggen;

import java.io.PrintStream;

/** Common abstract superclass of all expression types.
 * <p>
 * This class doesn't have an interface for you to use, but you can use the {@code Expr} type to refer to any JConfigGen AST expression.
 * To get started learning how to use the AST, see the <a href="package-summary.html">package description</a>.
 * </p>
 */
// After an Expr is constructed, it goes through 3 stages:
// 1 - Default stage
// 2 - Mode set stage
// 3 - Stats printed stage
// It starts in the default stage.
// Calling any of discard(), statize(), or useId() moves the Expr to the mode set stage.
// discard(), statize(), and useId() are 3 possible modes.
// The one other possible mode is the default mode, which is when none of the other 3 modes apply.
// The methods to set the mode are typically only safe to call on an Expr in the default stage, so a non-default mode only gets set once for a given Expr.
// Once the desired mode is set, (or you can leave the Expr in the default mode,) you print the supporting statements for an Expr with printStats().
// Calling printStats() moves the Expr to the stats printed stage.
// printStats() should only be called in either the default or mode set stage.
// It follows that you should only call printStats() once.
// Once the Expr is in the stats printed stage, you can call printExpr() to print the expression that finishes evaluating the Expr.
// This stage model does not forbid calling printExpr() multiple times, but care must be taken to evaluate an Expr only once, and in the proper order. See statize().
public abstract class Expr
{
	// Utility function to print a number of tabs.
	static void pTabs(PrintStream out,int tabs) {for (int i = 0;i < tabs;++i) out.print('\t');}

	// Utility function to complain about inline Java code.
	static void needType(int line,String c) throws InvalidConfigException {throw new InvalidConfigException(line,"Need type for code: " + c);}

	Expr() {}

	// Print the Java type of the Expr.
	// Throws an InvalidConfigException if the type cannot be determined.
	abstract void printType(PrintStream out) throws InvalidConfigException;

	// Returns true iff the Expr has helper statements.
	// This method can only be called in the default stage, before the mode is set.
	// As such, it is reporting whether the Expr will have helper statements if it remains in the default mode.
	// This method is used by TupleExpr in order to determine when prior Exprs need to be statized in order get the right order of evaluation.
	abstract boolean hasStats();

	// In the "discard" mode, the generated code for the Expr is optimized using the assumption that its result will not be used directly.
	// The result might still be used indirectly if it's assigned an id.
	// In other words, presumably Expr is being used for its side effects, and/or has an id for its result.
	// The main user of discard mode is Seq, since only the final expression of a sequence has its result directly available.
	// printExpr() is not used on a discarded Expr, so implementations of printExpr() don't need to make sense in discard mode.
	abstract void discard();

	// In the "statize" mode, all side effects of evaluating the Expr are done in the helper statements.
	// This serves two possible purposes:
	// - If an Expr's result is used multiple times, you don't want actually evaluate it multiple times.
	//   So you statize the Expr, so that you can use printExpr() multiple times, and the side effects will only happen once, in the helper statements.
	//   Note that some (side-effect-free) evaluation may still be repeated in the case of NoFX, but NoFX expressions should not have side effects.
	// - If some Expr "A" is supposed to evaluate before an Expr "B", and B has helper statements,
	//   B's helper statements might have side effects, which are supposed to evaluate after (all of) A.
	//   To ensure that the generated Java code respects this order of evaluation, all of A's side effects should be put in statements before B's helper statements.
	//   In other words, you would statize A, and printStats A before B.
	//   This is done in TupleExpr.
	abstract void statize();

	// In the "useId" mode, the generated code for the Expr will assign the result to the specified variable identifier.
	// This is used by Block, to avoid a redundant temporary variable and assignment which would otherwise arise in some cases.
	// Note that printExpr() can still be used in this mode, and should presumably print the specified identifier.
	// Unlike discard() and statize(), setting this mode may fail.
	// This method returns true if the mode was successfully set to "useId", with the specified identifer, and false otherwise, in which case the mode remains the default.
	abstract boolean useId(String i);

	// Print the helper statements that help evaluate Expr.
	// Helper statements need to be used because some of the features which the config language supports are not efficiently supported by Java at the expression level.
	// The main examples are sequences, variable definitions, and block scope.
	// I say Java doesn't support these at the expression level _efficiently_ because technically we could use anonymous inner classes to get classes, and thus statements, nested inside expressions.
	// Of course, using anonymous inner classes for that would be totally impractical.
	abstract void printStats(PrintStream out,int tabs) throws InvalidConfigException;

	// Print a Java expression the finishes evaluating the Expr.
	// By default, this may print an expression with side effects.
	// See statize().
	// Even if most of the work is done in the helper statements, we still need a Java expression to refer to the result.
	// It's often the case that this residual expression is simply a reference to the variable the result has been stored in.
	// In other cases, there are no helper statements, and everything is done in a big Java expression.
	abstract void printExpr(PrintStream out);
}
